#ifndef XG_FILE_H
#define XG_FILE_H
////////////////////////////////////////////////////////
#include "yaml.h"
#include "DateTime.h"
#include "MemQueue.h"


#define LogTrace(level, ...) LogThread::Instance()->trace(level, __VA_ARGS__)

#define TraceException(...) 																			\
{																										\
	Exception e(__VA_ARGS__);																			\
	LogTrace(eERR, "throw exception[%d][%s][%s:%d]", e.getErrorCode(), e.what(), __FILE__, __LINE__);	\
	throw e;																							\
}

typedef enum
{
	eDBG = 0,
	eTIP = 1,
	eINF = 2,
	eIMP = 3,
	eERR = 4
} E_LOGLEVEL;

class IFile : public Object
{
public:
	virtual void close() = 0;
	virtual int read(void* data, int size) = 0;
	virtual int write(const void* data, int size) = 0;

	virtual long long tell() const;
	virtual long long size() const;
	virtual long long seek(long long offset, int mode = SEEK_SET);
	
	template<class DataType> bool readObject(DataType& data)
	{
		if (IsSmallEndianSystem() || sizeof(DataType) == 1)
		{
			return sizeof(DataType) == read(&data, sizeof(DataType));
		}
		else
		{
			u_char msg[sizeof(DataType)];
			u_char* src = (u_char*)(msg);
			u_char* dest = (u_char*)(&data) + sizeof(DataType) - 1;

			CHECK_FALSE_RETURN(sizeof(DataType) == read(msg, sizeof(DataType)));

			for (int i = 0; i < sizeof(DataType); i++)
			{
				*dest-- = *src++;
			}

			return true;
		}
	}
	template<class DataType> bool writeObject(const DataType& data)
	{
		if (IsSmallEndianSystem() || sizeof(DataType) == 1)
		{
			return sizeof(DataType) == write(&data, sizeof(DataType));
		}
		else
		{
			u_char msg[sizeof(DataType)];
			u_char* src = (u_char*)(&data);
			u_char* dest = (u_char*)(msg) + sizeof(DataType) - 1;

			for (int i = 0; i < sizeof(DataType); i++)
			{
				*dest-- = *src++;
			}

			return sizeof(DataType) == write(msg, sizeof(DataType));
		}
	}
};

class File : public IFile
{
protected:
	FILE* handle;

public:
	~File();
	void close();
	bool create(const string& filename);
	bool open(const string& filename, const string& mode = "rb+");

	bool eof() const; 
	void flush() const;
	long long tell() const;
	long long size() const;
	int read(void* data, int size);
	int write(const void* data, int size);
	long long seek(long long offset, int mode = SEEK_SET);

	File()
	{
		handle = NULL;
	}
	FILE* getHandle() const
	{
		return handle;
	}
};

class XFile : public IFile
{
protected:
	HANDLE handle;

public:
	~XFile();
	void close();
	bool unlock();
	bool lock(bool waited = true);
	bool create(const string& filename);
	bool open(const string& filename, E_OPEN_MODE mode = eWRITE);

	void flush() const;
	long long tell() const;
	long long size() const;
	int read(void* data, int size);
	int write(const void* data, int size);
	long long seek(long long offset, int mode = SEEK_SET);

	XFile()
	{
		handle = (HANDLE)(-1);
	}
	HANDLE getHandle() const
	{
		return handle;
	}
};

class MemFile : public IFile
{
protected:
	int maxsz;
	int offset;
	int writed;
	char* data;
	SmartBuffer buffer;

public:
	~MemFile();
	void close();
	bool create(int maxsz);
	bool open(SmartBuffer buffer);
	bool open(void* data, int len);
	bool load(const string& filename);
	bool save(const string& filename) const;

	long long tell() const;
	long long size() const;
	bool truncate(int len);
	int read(void* data, int size);
	int write(const void* data, int size);
	long long seek(long long offset, int mode = SEEK_SET);

	MemFile()
	{
		maxsz = 0;
		offset = 0;
		writed = 0;
		data = NULL;
	}
	char* getData() const
	{
		return buffer.str();
	}
};

class CacheFile : public Object
{
	class Item
	{
	public:
		time_t ctime;
		time_t utime;
		SmartBuffer content;
	};

protected:
	int maxfilesize;
	CacheMap<string, Item> cachemap;

public:
	void clear()
	{
		cachemap.clear();
	}
	int getContent(const string& path, SmartBuffer& content)
	{
		time_t utime;

		return getContent(path, content, utime);
	}
	CacheFile(int maxlen = 100, int maxfilesize = 1024 * 1024)
	{
		this->init(maxlen, maxfilesize);
	}
	void init(int maxlen = 100, int maxfilesize = 1024 * 1024)
	{
		this->maxfilesize = stdx::minval(maxfilesize, XG_MEMFILE_MAXSZ);
		this->cachemap.init(maxlen);
	}

	int getContent(const string& path, SmartBuffer& content, time_t& utime);
};


class TextFile : public Object
{
protected:
	FILE* fp;
	bool flag;

	TextFile(const TextFile& log);
	TextFile& operator = (const TextFile& log);

public:
	void close();
	const TextFile& printf(const char* fmt, ...) const;

public:
	~TextFile()
	{
		close();
	}
	TextFile(FILE* fp)
	{
		this->fp = fp;
		this->flag = true;
	}
	TextFile(bool flag = true)
	{
		this->fp = NULL;
		this->flag = flag;
	}
	
public:
	long tell() const
	{
		return ftell(fp);
	}
	void flush() const
	{
		fflush(fp);
	}
	FILE* getHandle() const
	{
		return fp;
	}
	const TextFile& tr() const
	{
		fprintf(fp, "\n");
		if (flag) fflush(fp);
		return *this;
	}
	void setAutoFlush(bool flag)
	{
		this->flag = flag;
	}
	const TextFile& puts(const char* str) const
	{
		fprintf(fp, "%s\n", str);
		if (flag) fflush(fp);
		return *this;
	}
	const TextFile& puts(const string& str) const
	{
		return puts(str.c_str());
	}
	bool open(const string& filename, bool inited = false)
	{
		close();
		fp = fopen(filename.c_str(), inited ? "w+" : "a+");
		return fp ? true : false;
	}

	const TextFile& append(char ch) const
	{
		fputc(ch, fp);
		if (flag) fflush(fp);
		return *this;
	}
	const TextFile& append(char* str) const
	{
		fprintf(fp, "%s", str);
		if (flag) fflush(fp);
		return *this;
	}
	const TextFile& append(string& str) const
	{
		return append(str.c_str());
	}
	const TextFile& append(const char* str) const
	{
		fprintf(fp, "%s", str);
		if (flag) fflush(fp);
		return *this;
	}
	const TextFile& append(const string& str) const
	{
		return append(str.c_str());
	}
	template<class NUMBER_TYPE> const TextFile& append(NUMBER_TYPE val) const
	{
		return append(stdx::str(val));
	}

	const TextFile& operator << (char ch) const
	{
		return append(ch);
	}
	const TextFile& operator << (char* str) const
	{
		return append(str);
	}
	const TextFile& operator << (string& str) const
	{
		return append(str);
	}
	const TextFile& operator << (const char* str) const
	{
		return append(str);
	}
	const TextFile& operator << (const string& str) const
	{
		return append(str);
	}
	template<class NUMBER_TYPE> const TextFile& operator << (NUMBER_TYPE val) const
	{
		return append(stdx::str(val));
	}

	const TextFile& operator = (char ch) const
	{
		return append(" = ").append(ch).tr();
	}
	const TextFile& operator = (char* str) const
	{
		return append(" = ").puts(str);
	}
	const TextFile& operator = (string& str) const
	{
		return append(" = ").puts(str);
	}
	const TextFile& operator = (const char* str) const
	{
		return append(" = ").puts(str);
	}
	const TextFile& operator = (const string& str) const
	{
		return append(" = ").puts(str);
	}
	template<class NUMBER_TYPE> const TextFile& operator = (NUMBER_TYPE val) const
	{
		return append(" = ").append(stdx::str(val));
	}
};

class LogThread : public Thread
{
	friend class LoggerWorkItem;

protected:
	int flag;	// 0-FILE 1-CONSOLE 2-FILE AND CONSOLE
	int level;	// 0-DEBUG 1-TIPS 2-INFO 3-IMPORTANT 4-ERROR
	int index;
	XFile file;
	MemQueue mq;
	string logpath;
	string filename;
	size_t maxfilesize;
	SmartBuffer buffer;
	mutable SpinMutex mtx;
	function<void(const string&)> func;

	LogThread(const LogThread& log);
	LogThread& operator = (const LogThread& log);

	bool start();
	bool checkLogFile();
	string getLogFilePath();
	bool write(const char* msg, int len);
	bool print(const char* msg, int len);

public:
	virtual void run();
	virtual string getCurrentLogKey();
	virtual bool init(YAMLoader* file);
	virtual bool printf(const char* fmt, ...);
	virtual bool trace(int level, const string& msg);
	virtual bool trace(int level, const char* fmt, ...);
	virtual void setCurrentLogKey(const string& logkey);
	virtual void callback(function<void(const string&)> func);
	virtual bool init(const string& path, size_t maxsz = 10 * 1024 * 1024, bool sync = false);

	static LogThread* Instance();

	int size() const
	{
		SpinLocker lk(mtx);

		return mq.size();
	}
	bool empty() const
	{
		SpinLocker lk(mtx);

		return mq.empty();
	}
	void wait() const
	{
		while (true)
		{
			if (empty()) break;

			Sleep(1);
		}
	}
	int getLevel() const
	{
		return level;
	}
	int getLogFlag() const
	{
		return flag;
	}
	void setLevel(int level)
	{
		this->level = level;
	}
	void setLogFlag(int flag)
	{
		this->flag = flag;
	}
	int getMaxFileSize() const
	{
		return maxfilesize;
	}
	const char* getLogPath() const
	{
		return logpath.c_str();
	}
	LogThread() : flag(0), level(eINF), index(-1), maxfilesize(0)
	{
	}
};

class Timer
{
protected:
	FILE* fp;
	string msg;
	LogThread* log;
	unsigned long long ctime;

public:
	Timer(const char* msg = "time cost", FILE* fp = stdout, LogThread* log = NULL)
	{
		if (msg) this->msg = msg;
		this->ctime = GetTime();
		this->log = log;
		this->fp = fp;
	}
	~Timer()
	{
		if (msg.length() > 0)
		{
			long gap = getTimeGap();

			if (fp) fprintf(fp, "%s : %ldus\n", msg.c_str(), gap);
			if (log) LogTrace(eDBG, "%s : %ldus", msg.c_str(), gap);
		}
	}
	void setFile(FILE* fp)
	{
		this->fp = fp;
	}
	long getTimeGap() const
	{
		return GetTime() - ctime;
	}
	void setHeader(const string& msg)
	{
		this->msg = msg;
	}
	void setLogThread(LogThread* log)
	{
		this->log = log;
	}
};
////////////////////////////////////////////////////////
#endif